/**
 * 文件名称: touch.c
 * 摘    要: 与触摸相关的操作函数源文件
 * 来    源: huenrong
 *
 * 当前版本: 1.0 
 * 作    者: huenrong
 * 完成日期: 2019-07-28
 **/

#include <stdio.h>
#include <pthread.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#include "touch.h"
#include "../display/display.h"
#include "../display/framebuffer/framebuffer.h"


struct touch_info g_touch_info;     // 记录当前未处理的触摸信息
extern struct display_info g_display_info;      // 记录当前显示信息

/******************************************************************************
函数名称: get_touch_area
函数功能: 获取触摸点坐标所在区域
函数参数: axis_x: 获取到的触摸点坐标x值
          axis_y: 获取到的触摸点坐标y值
函数返回: 成功: 返回区域值代号
          失败: 返回-1
******************************************************************************/
static int get_touch_area(const unsigned int axis_x, const unsigned int axis_y)
{
    if ((SCREEN_DRAW_X_START <= axis_x) && (axis_x <= SCREEN_DRAW_X_END) && \
        (SCREEN_DRAW_Y_START <= axis_y) && (axis_y <= SCREEN_DRAW_Y_END))
    {
        return DRAW_AREA;
    }
    else if ((RED_PEN_AREA_X_START <= axis_x) && (axis_x <= RED_PEN_AREA_X_END) && \
             (RED_PEN_AREA_Y_START <= axis_y) && (axis_y <= RED_PEN_AREA_Y_END))
    {
        return RED_PEN_AREA;
    }
    else if ((GREEN_PEN_AREA_X_START <= axis_x) && (axis_x <= GREEN_PEN_AREA_X_END) && \
             (GREEN_PEN_AREA_Y_START <= axis_y) && (axis_y <= GREEN_PEN_AREA_Y_END))
    {
        return GREEN_PEN_AREA;
    }
    else if ((BLUE_PEN_AREA_X_START <= axis_x) && (axis_x <= BLUE_PEN_AREA_X_END) && \
             (BLUE_PEN_AREA_Y_START <= axis_y) && (axis_y <= BLUE_PEN_AREA_Y_END))
    {
        return BLUE_PEN_AREA;
    }
    else if ((BLACK_PEN_AREA_X_START <= axis_x) && (axis_x <= BLACK_PEN_AREA_X_END) && \
             (BLACK_PEN_AREA_Y_START <= axis_y) && (axis_y <= BLACK_PEN_AREA_Y_END))
    {
        return BLACK_PEN_AREA;
    }
    else if ((THIN_LINE_AREA_X_START <= axis_x) && (axis_x <= THIN_LINE_AREA_X_END) && \
             (THIN_LINE_AREA_Y_START <= axis_y) && (axis_y <= THIN_LINE_AREA_Y_END))
    {
        return THIN_LINE_AREA;
    }
    else if ((THICK_LINE_AREA_X_START <= axis_x) && (axis_x <= THICK_LINE_AREA_X_END) && \
             (THICK_LINE_AREA_Y_START <= axis_y) && (axis_y <= THICK_LINE_AREA_Y_END))
    {
        return THICK_LINE_AREA;
    }
    else if ((ERASER_AREA_X_START <= axis_x) && (axis_x <= ERASER_AREA_X_END) && \
             (ERASER_AREA_Y_START <= axis_y) && (axis_y <= ERASER_AREA_Y_END))
    {
        return ERASER_AREA;
    }
    else if ((RESET_AREA_X_START <= axis_x) && (axis_x <= RESET_AREA_X_END) && \
             (RESET_AREA_Y_START <= axis_y) && (axis_y <= RESET_AREA_Y_END))
    {
        return RESET_AREA;
    }
    
    return -1;
}

/******************************************************************************
函数名称: get_touch_position
函数功能: 获取触摸点坐标
函数参数: axis_x: 获取到的触摸点坐标x值
          axis_y: 获取到的触摸点坐标y值
函数返回: 成功: 返回0
          失败: 返回-1
******************************************************************************/
static int get_touch_position(unsigned int *axis_x, unsigned int *axis_y)
{
    int fd = -1;        // 触摸屏设备文件描述符
    int ret = -1;
    struct input_event ev;

    fd = open(TOUCHSCREEN_DEVICE, O_RDONLY);        // 打开触摸屏设备
	if (fd < 0)
	{
		perror("touch_thread open error");

		return -1;
	}
    
    do
    {
        memset(&ev, 0, sizeof(struct input_event));
        ret = read(fd, &ev, sizeof(struct input_event));
        if (sizeof(struct input_event) != ret)
		{
			perror("get_touch_position read error");
			close(fd);

			return -1;
		}
        else if ((sizeof(struct input_event) == ret) && (EV_ABS == ev.type))
        {
            if (ABS_X == ev.code)
            {
                *axis_x = ev.value;
            }
            else if (ABS_Y == ev.code)
            {
                *axis_y = ev.value;
            }
        }
    } while ((*axis_x <= 0) || (*axis_y <= 0));

    close(fd);

    return 0;
}

/******************************************************************************
函数名称: eraser_function
函数功能: 擦除操作处理函数
函数参数: 无
函数返回: 无
******************************************************************************/
static void eraser_function(const unsigned int axis_x, const unsigned int axis_y)
{
    int touch_area = -1;    // 获取到的触摸区域

    touch_area = get_touch_area(axis_x, axis_y);

    switch (touch_area)
    {
        case DRAW_AREA:     // 触摸在有效显示区域, 则将触摸点附近擦除
        {
            framebuffer_draw_area(axis_x - 5, axis_x + 5, axis_y - 5, axis_y + 5, WHITE);

            break;
        }

        case ERASER_AREA:       // 触摸橡皮擦区域后, 标志位反转
        {
            if (0 == g_display_info.eraser_flag)
            {
                g_display_info.eraser_flag = 1;
            }
            else
            {
                g_display_info.eraser_flag = 0;
            }
        }

        case RESET_AREA:        // 复位区域
        {
            if (0 == g_display_info.eraser_flag)
            {
                g_display_info.eraser_flag = 1;
            }
            else
            {
                g_display_info.eraser_flag = 0;
            }

            // 未处理触摸位置信息buf赋值为-1
            memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));

            framebuffer_draw_area(SCREEN_DRAW_X_START, SCREEN_DRAW_X_END, 
                                    SCREEN_DRAW_Y_START, SCREEN_DRAW_Y_END, WHITE);
            
            break;
        }
        
        default:
            break;
    }
    
}

/******************************************************************************
函数名称: touch_thread
函数功能: 触摸操作线程
函数参数: 无
函数返回: 无
******************************************************************************/
static void touch_thread(void)
{
    int ret = -1;
    unsigned int axis_x = -1;        // 触摸点坐标x值
    unsigned int axis_y = -1;        // 触摸点坐标y值
    int touch_area = -1;    // 获取到的触摸区域

    // 未处理触摸位置信息buf赋值为-1
    memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));
    // 队列从0位置开始使用
    g_touch_info.header = 0;
    g_touch_info.tail = 0;

    while (1)
    {
        axis_x = 0;
        axis_y = 0;
        ret = get_touch_position(&axis_x, &axis_y);     // 获取触摸点坐标
        if (0 == ret)
        {
            // 如果是触摸到了功能图标, 则更新功能
            // 如果触摸点不是功能图标位置, 则需要显示内容
            printf("touch position: (%d, %d)\r\n", axis_x, axis_y);
            touch_area = get_touch_area(axis_x, axis_y);        // 根据触摸点判断触摸区域
            switch (touch_area)
            {
                case DRAW_AREA:         // 有效绘制区域(记录按下的位置并显示)
                {
                    printf("this is draw area\r\n");

                    // 将触摸位置记录到队列尾部
                    g_touch_info.position[g_touch_info.tail].x = axis_x;
                    g_touch_info.position[g_touch_info.tail].y = axis_y;
                    
                    // 队列尾后移
                    g_touch_info.tail++;
                    if (g_touch_info.tail >= TOUCH_INFO_MAX_LENGTH)
                    {
                        g_touch_info.tail = 0;
                    }

                    break;
                }

                case RED_PEN_AREA:      // 红色笔区域
                {
                    printf("this is red pen area\r\n");

                    // 未处理触摸位置信息buf赋值为-1
                    memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));

                    // 更新当前选中的画笔颜色
                    g_display_info.draw_color = RED;
                    update_selected_color();
                    
                    break;
                }

                case GREEN_PEN_AREA:    // 绿色笔区域
                {
                    printf("this is green pen area\r\n");

                    // 未处理触摸位置信息buf赋值为-1
                    memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));

                    // 更新当前选中的画笔颜色
                    g_display_info.draw_color = GREEN;
                    update_selected_color();
                    
                    break;
                }

                case BLUE_PEN_AREA:     // 蓝色笔区域
                {
                    printf("this is blue pen area\r\n");

                    // 未处理触摸位置信息buf赋值为-1
                    memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));

                    // 更新当前选中的画笔颜色
                    g_display_info.draw_color = BLUE;
                    update_selected_color();
                    
                    break;
                }

                case BLACK_PEN_AREA:    // 黑色笔区域
                {
                    printf("this is black pen area\r\n");

                    // 未处理触摸位置信息buf赋值为-1
                    memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));

                    // 更新当前选中的画笔颜色
                    g_display_info.draw_color = BLACK;
                    update_selected_color();
                    
                    break;
                }

                case THIN_LINE_AREA:    // 细线区域
                {
                    printf("this is thin line area\r\n");

                    // 未处理触摸位置信息buf赋值为-1
                    memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));

                    // 更新当前选中的画笔粗细
                    g_display_info.brush_thickness = THIN_LINE;
                    update_selected_line();
                    
                    break;
                }

                case THICK_LINE_AREA:   // 粗线区域
                {
                    printf("this is thick line area\r\n");

                    // 未处理触摸位置信息buf赋值为-1
                    memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));
                    
                    // 更新当前选中的画笔粗细
                    g_display_info.brush_thickness = THICK_LINE;
                    update_selected_line();

                    break;
                }

                case ERASER_AREA:       // 橡皮擦区域
                {
                    printf("this is eraser area\r\n");
                    
                    // 未处理触摸位置信息buf赋值为-1
                    memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));

                    // 点击橡皮擦区域后, 标志位反转
                    if (0 == g_display_info.eraser_flag)
                    {
                        g_display_info.eraser_flag = 1;
                    }
                    else
                    {
                        g_display_info.eraser_flag = 0;
                    }

                    update_eraser_area();

                    while (g_display_info.eraser_flag)
                    {
                        axis_x = 0;
                        axis_y = 0;
                        ret = get_touch_position(&axis_x, &axis_y);     // 获取触摸点坐标
                        if (0 == ret)
                        {
                            eraser_function(axis_x, axis_y);
                        }
                    }

                    update_eraser_area();
                    
                    break;
                }

                case RESET_AREA:        // 复位区域
                {
                    printf("this is reset area\r\n");

                    // 未处理触摸位置信息buf赋值为-1
                    memset(&g_touch_info.position, -1, TOUCH_INFO_MAX_LENGTH * sizeof(struct touch_position));

                    framebuffer_draw_area(SCREEN_DRAW_X_START, SCREEN_DRAW_X_END, 
                                          SCREEN_DRAW_Y_START, SCREEN_DRAW_Y_END, WHITE);
                    
                    break;
                }

                default:
                    break;
            }
        }
    }
}

/******************************************************************************
函数名称: creat_touch_thread
函数功能: 创建触摸操作处理线程
函数参数: 无
函数返回: 成功: 返回0
          失败: 返回-1
******************************************************************************/
int creat_touch_thread(void)
{
    int ret = -1;
    unsigned char fail_num = 0;     // 创建线程失败次数
    pthread_t touch_thread_id = -1;        // 触摸操作线程ID

    while (1)
    {
        // 创建触摸操作线程
        ret = pthread_create(&touch_thread_id, NULL, (void *)touch_thread, NULL);
        if (0 != ret)
        {
            printf("creat_touch_thread pthread_create fail!\n");
            usleep(50 * 1000);
            fail_num++;
            if (3 == fail_num)      // 创建3次都失败则认为失败
            {
                return -1;
            }
        }
        else if (0 == ret)      // 线程创建成功，分离线程，并退出
        {
            pthread_detach(touch_thread_id);       // 线程分离，结束时自动回收资源

            return 0;
        }
    }
}
